////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//                                                                                                                            //
//                                                         WNProject                                                          //
//                                                                                                                            //
//            This file is distributed under the BSD 2-Clause open source license. See License.txt for details.               //
//                                                                                                                            //
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#ifndef __WN_INTEROP_HELPERS_H__
#define __WN_INTEROP_HELPERS_H__

#include "WNScripting/inc/WNScriptingConfig.h"
#include "WNCore/inc/WNAssert.h"
#include "WNMath/inc/WNBasicMath.h"

namespace WNScriptInterop {
    extern WN_BYTE* GetStackArrayReturnLocation(WN_SIZE_T _size);

    ///Note all script array COPIES share the same memory This is memory that is generally allocated for the 
    ///scripting engine
    template<typename T> class ScriptArray {
    public:
        static WN_SIZE_T GetAdditionalElements() {
            return(static_cast<WN_SIZE_T>(ceill(static_cast<WN_FLOAT32>(sizeof(WNS_INT_CONST)) / static_cast<WN_FLOAT32>(sizeof(T)))));
        }

        ScriptArray(const ScriptArray<T>& _other):
            mArrayData(_other.arrayData),
            mLength(_other.length),
            mStartOffset(_other.startOffset) {
        }

        ScriptArray(WN_BYTE* _array) {
           mArrayData = reinterpret_cast<T*>(_array);
           mStartOffset = static_cast<WN_SIZE_T>(ceill(static_cast<WN_FLOAT32>(sizeof(WNS_INT_CONST)) / static_cast<WN_FLOAT32>(sizeof(T))));
           mLength = *(reinterpret_cast<WNS_INT_CONST*>(mArrayData));
        }

        ScriptArray(WN_BYTE* _array, WN_SIZE_T _initialSize) {
            mArrayData = reinterpret_cast<T*>(_array);
            mLength = _initialSize;
            mStartOffset = static_cast<WN_SIZE_T>(ceill(static_cast<WN_FLOAT32>(sizeof(WNS_INT_CONST)) / static_cast<WN_FLOAT32>(sizeof(T))));
            *(reinterpret_cast<WNS_INT_CONST*>(mArrayData)) = static_cast<WNS_INT_CONST>(_initialSize);
        }

        WN_SIZE_T Length() {
            return(mLength);
        }

        T& operator[] (WN_SIZE_T val) {
            WN_DEBUG_ASSERT(val < mLength);
            return(mArrayData[mStartOffset + val]);
        }
    
        WN_BYTE* GetRawData() {
            return(reinterpret_cast<WN_BYTE*>(mArrayData));
        }

        WN_UINT32 GetSizeInBytes() {
            return(static_cast<WN_UINT32>(sizeof(T) * (mLength + mStartOffset)));
        }

    private:
        T* mArrayData;
        WN_SIZE_T mLength;
        WN_SIZE_T mStartOffset;
    };

    template<typename T> ScriptArray<T> CreateScriptArrayForStack(WN_SIZE_T _size) {
        WN_SIZE_T stackSize = _size + ScriptArray<T>::GetAdditionalElements();
        WN_BYTE* loc = GetStackArrayReturnLocation(stackSize);

        return(ScriptArray<T>(loc, _size));
    }
}

#endif // __WN_INTEROP_HELPERS_H__